// ==========
// Team - 7
// ==========
//
// Part 4: Gameplay and Animation
//
// Jaime Francisco Jaramillo 
// Kevin El Hage
// Hugo	Goncalves de Oliveira
// Franklin Negrete
// David Gauthier
//

#include "cugl.h"
#include "Map.h"
#include "Robot.h"
#include "ControlUnit.h"
#include "Timer.h"
#include <sstream>
#include <iomanip>
#include <map>

using std::stringstream;
using std::cout;
using std::endl;
using std::ends;

using std::vector;

#define PI 3.14

//-----------------------------------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------------------------------
void drawString(const char *str, int x, int y, float color[4], void *font);
void printHelp();
void showFPS();
void buildRobot();
 
// map object (reads the platform's tile and dimension config from an array)
static Map *map;

// default window dimensions 
static int windowWidth = 1024;
static int windowHeight = 768; 

int mainWindow;

// Perspective Projection settings
static GLfloat fovy = 45;	    // field-of-view
static GLfloat nPlane = 0.1;	// near plane
static GLfloat fPlane = 300.0;	// far plane

// flags
static enum shadingModes{ WIREFRAME, FLAT, SMOOTH };
static shadingModes shadingMode = SMOOTH;		      // Smooth by default
static bool lightingEnabled = 1;
static bool allLightPostsSwitch = 1;
static int lightPostLightsState[] = {1, 1, 1, 1, 1};		  // lighting on by default
static bool ambientLightEnabled = 1;
static bool orbitingCamLight =0;
static bool texturing = 1;
static bool cuLightOn = 1;
static bool isFullScreen = 0;
static bool mouseLook = 0;									// mouseLook OFF by default
static bool gameOver = 0;
static bool helpPrompt = 0;
static bool robotLightOn = 0;
															
// cameras
static enum cameras{freeCam, orbitingCam, lPostCamNW, lPostCamNE, lPostCamSW, lPostCamSE, robotCam, cuCam};		    // all cameras
static cameras currentCam = freeCam;																			// default camera
static const GLfloat camForwardSpeed = 1.5;
static cugl::Camera freecam(cugl::Point(25, 53, 78), cugl::Point(25, 0, 25), cugl::Vector(0, 1, -1).unit());	// free/main cam
static cugl::Camera orbCam1(cugl::Point(25, 53, 78), cugl::Point(25, 0, 25), cugl::Vector(0, 1, -1).unit());	// orbiting/circling cam
static cugl::Camera* robotCam1 = new cugl::Camera();	// robot cam

// lights (not in the objects themselves--like lightposts and the control unit)
spotLight orbitingLight;

// other globals
vector<Robot*> robots;
std::map<int, Robot*> mapRobots;
std::map<int, ControlUnit*> mapControlUnit;
int currentIDRobot;
vector<Robot*>::iterator robotIterator;
ControlUnit *ctrlUnit;
std::string currentLvl;

static GLfloat rotationUp = 0.0;
static GLfloat rotationLeft = 0.0;
void *font = GLUT_BITMAP_8_BY_13;
static int mouseX = 0;
static int mouseY = 0;
//---------------------------------------------------------------------------------------------------------------------



//---------------------------------------------------------
// LIGHTING
//---------------------------------------------------------
void configureLights()
{
	// default settings for orbiting light
	orbitingLight.setPosition(orbCam1.getPosition()[0], orbCam1.getPosition()[1], orbCam1.getPosition()[2]);
	orbitingLight.setDirection(-orbCam1.getPosition()[0]+(map->getWidth()/2), -orbCam1.getPosition()[1], -orbCam1.getPosition()[2]+(map->getLength()/2));
	orbitingLight.setCutOffAngle(7);
}

void lights()
{
	// global ambient light
	GLfloat global_ambient[4];
	global_ambient[3] = 1.0;
	if(ambientLightEnabled)
	{
		global_ambient[0] = global_ambient[1] = global_ambient[2] = 0.4f;		// turn on
	}
	else
	{
		global_ambient[0] = global_ambient[1] = global_ambient[2] = 0.0f;		// turn off
	}
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);

	// all lights
	if(lightingEnabled)
	{
		// light posts'lights
		if(lightPostLightsState[0]) map->lightPostNW->turnOn(); else map->lightPostNW->turnOff();
		if(lightPostLightsState[1]) map->lightPostNE->turnOn(); else map->lightPostNE->turnOff();
		if(lightPostLightsState[2]) map->lightPostSW->turnOn(); else map->lightPostSW->turnOff();
		if(lightPostLightsState[3]) map->lightPostSE->turnOn(); else map->lightPostSE->turnOff();

		// orbiting light
		if(orbitingCamLight) orbitingLight.turnOn(); else orbitingLight.turnOff();

		// control unit light
		if(cuLightOn) ctrlUnit->getLight()->turnOn(); else ctrlUnit->getLight()->turnOff();

		// robot light
		if(!gameOver )
		{
			if(robotLightOn) robots.at(0)->turnLightOn(); else robots.at(0)->turnLightOff();
			if(robotLightOn) robots.at(1)->turnLightOn(); else robots.at(1)->turnLightOff();
		}
	}

}

//---------------------------------------------------------
// CAMERAS
//---------------------------------------------------------
void cameraOn()
{
	// turn on the currently selected camera

	cugl::Camera* current;
	switch(currentCam)
	{
		case orbitingCam:
			current = &orbCam1;
			break;
		case lPostCamNW:
			current = map->lightPostNW->getCam();			// lightpost NW camera
			break;
		case lPostCamNE:
			current = map->lightPostNE->getCam();			// lightpost NE camera
			break;
		case lPostCamSW:
			current = map->lightPostSW->getCam();			// lightpost SW camera
			break;
		case lPostCamSE:
			current = map->lightPostSE->getCam();			// lightpost SE camera
			break;
		case robotCam:
			current = robotCam1;
			break;
		case cuCam:
			current = ctrlUnit->getCamera();
			break;
		case freeCam:
		default:
			freecam.setSkyBoxSize(map->getSkyBoxSize());
			current = &freecam;
			break;
	}

	current->apply();
	current->idle();
}


//--------------------------------------------------------
// DRAW SCENE
//--------------------------------------------------------
void drawScene()
{	
	glMatrixMode(GL_PROJECTION);
	glViewport(0, 0, windowWidth, windowHeight);
	glMatrixMode(GL_MODELVIEW);
	
	// camera
	cameraOn();

	// lighting
	lights();

	// enable texturing
	if(texturing)
		glEnable(GL_TEXTURE_2D);

	// Drawing the platform/scene/map
	map->display();
	
	// control unit
	if(!ctrlUnit->onRobot() || currentCam != robotCam)
		ctrlUnit->display();

	for(vector<Robot*>::const_iterator it = robots.begin(); it != robots.end();)
	{
		if(!(*it)->deleteRobot())
		{
			(*it)->display();
			(*it)->displayLife();
			++it;
		}
		else
		{
			int tmpID = (*it)->getID();
			int currentTmpID = (*robotIterator)->getID();
			
			// remove all the pointers
			delete *it;
			it = robots.erase(it);
			mapRobots.erase(tmpID);

			// set the new robotIterator
			for (vector<Robot*>::iterator i = robots.begin(); i!=robots.end(); ++i)
			{
				if ((*i)->getID() == currentTmpID)
				{
					robotIterator = i;
					//i = robots.end();
				}
			}

			gameOver = 1;
		}
	}

	// draw scene objects
	//scenHelp->draw();

	// disable texturing
	glDisable(GL_TEXTURE_2D);	

	if(!gameOver)
		showFPS();

	if(helpPrompt)
		printHelp();
}


//---------------------------------------------------------
// DISPLAY
//---------------------------------------------------------
void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();				// Reset The Projection Matrix
	if(!isFullScreen)
		gluPerspective(fovy, (GLdouble)4/3, nPlane, fPlane);
	else
		gluPerspective(fovy, (GLdouble)16/9, nPlane, fPlane);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();				// Reset The ModelView matrix

	drawScene();

	glFinish();

	if(gameOver)
	{
		// display Game Over msg
		std::string gOverMsg = "YOU WON !";
		//void *gOFont = GLUT_BITMAP_HELVETICA_18;
		void *gOFont = GLUT_BITMAP_TIMES_ROMAN_24;
		float color[4] = {1, 1, 0, 1};
		drawString(gOverMsg.c_str(), 180, 150, color, gOFont);

		// turn off all lights
		ambientLightEnabled = 1;
		for(int i=0; i<4; ++i)
		{
			lightPostLightsState[i] = 0;
		}
		cuLightOn = 0;
		orbitingCamLight = 0;
	}

    glutSwapBuffers();
    glutPostRedisplay();
}


void reshape (int w, int h)
{
   //glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
	windowWidth = w;
	windowHeight = h;
   glutPostRedisplay();
}

//---------------------------------------------------------
// HELP
//---------------------------------------------------------
static bool helpWritten = 0;
static GLuint DLindexHelp;
void printHelp()
{
	if(!helpWritten)
	{
		// make a new display list
		DLindexHelp = glGenLists(1);
		glNewList(DLindexHelp, GL_COMPILE);


		static stringstream ss;
		static std::string s;
		double elapsedTime;


		float color[4] = {1, 1, 0, 1};

		ss << "\nCamera Selection:\n";
		ss << "(1) Free Camera (default)\n";
		ss << "(2) Orbiting Camera\n";
		ss << "(3) Lighpost NW Camera\n";
		ss << "(4) Lighpost NE Camera\n";
		ss << "(5) Lighpost SW Camera\n";
		ss << "(6) Lighpost SE Camera\n";
		ss << "(7) Control Unit Camera\n";
		ss << "(F1) Robot Camera 1\n";
		ss << "(F2) Robot Camera 2\n";
	
		ss << "\nFree Camera Controls:\n";
		ss << "(q , e) Roll\n";
		ss << "(a , d) Yaw\n";
		ss << "(w , s) Pitch\n";
		ss << "([ , ]) Strafe\n";
		ss << "({ , }) Move Up/Down\n";
		ss << "(f) Move Forwards\n";
		ss << "(v) Move Backwards\n";
		ss << "(r) Reset (default position)\n";
		ss << "($) Place camera on platform level\n";
	
		ss << "\nOrbiting Camera Controls:\n";
		ss << "([) Rotate Camera Left\n";
		ss << "(]) Rotate Camera Right\n";
	
		ss << "\nFirstPerson (robot) Controls:\n";
		ss << "(w , s) Yaw Camera\n";
		ss << "(a , d) Pitch Camera\n";
		ss << "(p) Switch Textures\n";
		ss << "(Space Bar) Fire\n";
		ss << "(*) Fire Cannons\n";
		ss << "(-) Fire Missiles\n";
		ss << "(+) Fire Phasers\n";
		ss << "(Left Arrow, Right Arrow) Rotate -- with CU attached\n";
		ss << "(UpArrow, DownArrow) Move -- with CU attached\n";
		
		ss << "^";  // new column

		ss << "\nControl Unit Controls:\n";
		ss << "(<) Move Up/Detach\n";
		ss << "(>) Move Down\n";
		ss << "(Left Arrow) Move Left\n";
		ss << "(Right Arrow) Move Right\n";
		ss << "(Up Arrow) Move Up\n";
		ss << "(Down Arrow) Move Down\n";

		ss << "\nLighting:\n";
		ss << "(\\) Turn Lighting ON/OFF\n";
		ss << "(l) Turn ambient light ON/OFF\n";
		ss << "(L) Turn ALL light posts ON/OFF\n";
		ss << "(c) Turn orbiting spotlight ON/OFF\n";
		ss << "(:) Turn NW light post ON/OFF\n";
		ss << "(') Turn NE light post ON/OFF\n";
		ss << "(.) Turn SE light post ON/OFF\n";
		ss << "(/) Turn SW light post ON/OFF\n";
		ss << "(i) Control Unit light ON/OFF\n";
		ss << "(k) Turn Robots' lights ON/OFF" << std::endl;
	
		ss << "\nOther Options:\n";
		ss << "(o) Switch all Robots textures\n";
		ss << "(h) Show/Hide Help\n";
		ss << "(m) Wireframe/Flat/Smooth Shading Toggle (smooth by default)\n";
		ss << "(t) Turn Texture Mapping ON/OFF\n";
		ss << "(F7) Toggle between windowed and full screen modes\n";
		ss << "(right click) Restart/Choose level menu --windowed mode only\n";
		ss << "(esc) Exit\n"; 
		ss << ends; 
        ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield);
		drawString(ss.str().c_str(), 5, 294, color, font);

		glEndList();

		helpWritten =1;
	}
	else
	{
		glPushMatrix();
			glCallList(DLindexHelp);
		glPopMatrix();
	}

}

void help()
{
	// Help message/menu
	std::cout<< "============================================" << std::endl;
	std::cout<< "Team 7 - Part 4: Gameplay and Animation" << std::endl;
	std::cout<< "============================================" << std::endl;
	
	std::cout<< "\nCamera Selection:" << std::endl;
	std::cout<< "(1) Free Camera (default)" << std::endl;
	std::cout<< "(2) Orbiting Camera" << std::endl;
	std::cout<< "(3) Lighpost NW Camera" << std::endl;
	std::cout<< "(4) Lighpost NE Camera" << std::endl;
	std::cout<< "(5) Lighpost SW Camera" << std::endl;
	std::cout<< "(6) Lighpost SE Camera" << std::endl;
	std::cout<< "(7) Control Unit Camera" << std::endl;
	std::cout<< "(F1) Robot Camera 1" << std::endl;
	std::cout<< "(F2) Robot Camera 2" << std::endl;
	
	std::cout<< "\nFree Camera Controls:" << std::endl;
	std::cout<< "(q , e) Roll" << std::endl;
	std::cout<< "(a , d) Yaw" << std::endl;
	std::cout<< "(w , s) Pitch" << std::endl;
	std::cout<< "([ , ]) Strafe" << std::endl;
	std::cout<< "({ , }) Move Up/Down" << std::endl;
	std::cout<< "(f) Move Forwards" << std::endl;
	std::cout<< "(v) Move Backwards" << std::endl;
	std::cout<< "(r) Reset (default position)" << std::endl;
	std::cout<< "($) Place camera on platform level" << std::endl;
	
	std::cout<< "\nOrbiting Camera Controls:" << std::endl;
	std::cout<< "([) Rotate Camera Left" << std::endl;
	std::cout<< "(]) Rotate Camera Right"  << std::endl;
	
	std::cout<< "\nFirstPerson (robot) Controls:" << std::endl;
	std::cout<< "(w , s) Yaw Camera" << std::endl;
	std::cout<< "(a , d) Pitch Camera" << std::endl;
	std::cout<< "(p) Switch Textures" << std::endl;
	std::cout<< "(Space Bar) Fire" << std::endl;
	std::cout<< "(*) Fire Cannons" << std::endl;
	std::cout<< "(-) Fire Missiles" << std::endl;
	std::cout<< "(+) Fire Phasers" << std::endl;
	std::cout<< "(Left Arrow, Right Arrow) Rotate -- with CU attached" << std::endl;
	std::cout<< "(UpArrow, DownArrow) Move -- with CU attached" << std::endl;
	
	std::cout<< "\nControl Unit Controls:" << std::endl;
	std::cout<< "(<) Move Up/Detach" << std::endl;
	std::cout<< "(>) Move Down" << std::endl;
	std::cout<< "(Left Arrow) Move Left" << std::endl;
	std::cout<< "(Right Arrow) Move Right" << std::endl;
	std::cout<< "(Up Arrow) Move Up" << std::endl;
	std::cout<< "(Down Arrow) Move Down" << std::endl;

	std::cout<< "\nLighting:" << std::endl;
	std::cout<< "(\\) Turn Lighting ON/OFF" << std::endl;
	std::cout<< "(l) Turn ambient light ON/OFF" << std::endl;
	std::cout<< "(L) Turn ALL light posts ON/OFF" << std::endl;
	std::cout<< "(c) Turn orbiting spotlight ON/OFF" << std::endl;
	std::cout<< "(:) Turn NW light post ON/OFF" << std::endl;
	std::cout<< "(') Turn NE light post ON/OFF" << std::endl;
	std::cout<< "(.) Turn SE light post ON/OFF" << std::endl;
	std::cout<< "(/) Turn SW light post ON/OFF" << std::endl;
	std::cout<< "(i) Control Unit light ON/OFF" << std::endl;
	std::cout<< "(k) Turn Robots' lights ON/OFF" << std::endl;
	
	std::cout<< "\nOther Options:" << std::endl;
	std::cout<< "(o) Switch all Robots textures" << std::endl;
	std::cout<< "(h) (re)Print help" << std::endl;
	std::cout<< "(m) Wireframe/Flat/Smooth Shading Toggle (smooth by default)" << std::endl;
	std::cout<< "(t) Turn Texture Mapping ON/OFF" << std::endl;
	std::cout<< "(F7) Toggle between windowed and full screen modes" << std::endl;
	std::cout<< "(right click) Restart/Choose level menu --windowed mode only" << std::endl;
	std::cout<< "(esc) Exit" << std::endl; 
}


//---------------------------------------------------------
// KEYBOARD functions
//---------------------------------------------------------
void keyboardRobot(unsigned char key)
{
	Robot *currentRobot = ctrlUnit->getCurrentRobot();
	if(!currentRobot)
		currentRobot = (*robotIterator);
	switch(key)
	{
		case 'w':
			currentRobot->tiltUp();
			robotCam1 = currentRobot->camera;
			glutPostRedisplay();
			break;
		case 'a':   
			currentRobot->rotateCameraLeft();;
			robotCam1 = currentRobot->camera;
			glutPostRedisplay();
			break;
		case 's':   
			currentRobot->tiltDown();
			robotCam1 = currentRobot->camera;
			glutPostRedisplay();
			break;
		case 'd':
			currentRobot->rotateCameraRight();
			robotCam1 = currentRobot->camera;
			glutPostRedisplay();
			break;
		case 'u':
			currentRobot->moveUp();
			break;
		case 'j':
			currentRobot->moveDown();
			break;
		case 'p':
			currentRobot->switchTexture();
			break;
		case '-':
			currentRobot->fireMissiles();
			break;
		case '*':
			currentRobot->fireCannon();
			break;
		case '+':
			currentRobot->firePhaser();
			break;
		case 32:
			// spacebar
			currentRobot->fire();
			break;
		default:
			break;
	}
}

void mouseMotionCB(int x, int y)
{
	glutSetCursor(GLUT_CURSOR_NONE); 

	if(currentCam == freeCam)
	{
		GLfloat speed = 0.020;
		GLfloat upSpeed = 0.020;

		GLfloat angleUp = 0;
		GLfloat angleLeft = 0;

		if(x < mouseX)
		{
			angleLeft = speed;
			mouseX = x;
		}
		else if(x > mouseX)
		{
			angleLeft = -speed;
			mouseX = x;
		}

		if(y < mouseY)
		{
			angleUp = upSpeed;
			mouseY = y;
		}
		else if(y > mouseY)
		{
			angleUp = -upSpeed;
			mouseY = y;
		}

		freecam.moveFPS(angleLeft, angleUp);
		if(x>=windowWidth-5 || x<=5 || y<=5 ||y>=windowHeight-5)
		{
			glutWarpPointer(windowWidth / 2, windowHeight / 2);
			mouseX = 0;
			mouseY = 0;
		}
		glutPostRedisplay();
	}
}


void keyboardFreeCam(unsigned char key)
{
	switch(key)
		{
		  case 'X':		//enable/disable mouse look (behaves like FPS)
			 if(!mouseLook)
			 {
				glutPassiveMotionFunc(mouseMotionCB);
				mouseLook = 1;
				glutPostRedisplay();
			 }
			 else
			 {
				glutPassiveMotionFunc(0);
				mouseLook = 0;
				glutPostRedisplay();
			 }
			break;
		  case 'f':   
			// move camera FORWARD
			freecam.moveForward(camForwardSpeed);
			glutPostRedisplay();
			break;
		  case 'v':   
			// move camera BACKWARDS
			freecam.moveForward(-camForwardSpeed);
			glutPostRedisplay();
			break;
		  case '[':   
			// move camera LEFT
			freecam.moveLeft(camForwardSpeed);
			glutPostRedisplay();
			break;
		  case ']':   
			// move camera RIGHT
			freecam.moveLeft(-camForwardSpeed);
			glutPostRedisplay();
			break;
		case '{':   
			// move camera UP
			freecam.moveUp(camForwardSpeed);
			glutPostRedisplay();
			break;
		case '}':   
			// move camera DOWN
			freecam.moveUp(-camForwardSpeed);
			glutPostRedisplay();
			break;
		  case 'a':   
			// YAW camera left
			freecam.panLeft(0.1);
			glutPostRedisplay();
			break;
		  case 'd':   
			// YAW camera right
			freecam.panLeft(-0.1);
			glutPostRedisplay();
			break;
		  case 'w':   
			// PITCH camera UP
			freecam.tiltUp(0.05);
			glutPostRedisplay();
			break;
		  case 's':   
			// PITCH camera DOWN
			freecam.tiltUp(-0.05);
			glutPostRedisplay();
			break;
		  case 'q':   
			// ROLL camera left
			freecam.rollLeft(0.05);
			glutPostRedisplay();
			break;
		  case 'e':   
			// ROLL camera right
			freecam.rollLeft(-0.05);
			glutPostRedisplay();
			break;
		  case '$':
			  freecam.set(cugl::Point(49, 3, 49), cugl::Point(48, 3, 48), cugl::Vector(0, 1, 0).unit()); 
			  glutPostRedisplay();
			break;
		 case 'z': // FOVY zoom OUT
			  fovy += 0.9; 
			  glutPostRedisplay();
			break;
		  case 'Z':  // FOVY zoom IN
			  fovy -= 0.9; 
			  glutPostRedisplay();
			break;
		  default:
			break;
		}
}
void keyboardOrbitingCam(unsigned char key)
{
	switch(key) 
		{
			case '[' :
				// Rotate camera clock-wise around the center on the x,z plane
				orbCam1.rotateAroundLookAt(0.15);
				// update the orbiting light
				orbitingLight.setPosition(orbCam1.getPosition()[0], orbCam1.getPosition()[1], orbCam1.getPosition()[2]);
				orbitingLight.setDirection(-orbCam1.getPosition()[0]+(map->getWidth()/2), -orbCam1.getPosition()[1], -orbCam1.getPosition()[2]+(map->getLength()/2));
				glutPostRedisplay();
				break;
			case ']' :
				// Rotate camera COUNTER clock-wise around the center on the x,z plane
				orbCam1.rotateAroundLookAt(-0.15);
				// update the orbiting light
				orbitingLight.setPosition(orbCam1.getPosition()[0], orbCam1.getPosition()[1], orbCam1.getPosition()[2]);
				orbitingLight.setDirection(-orbCam1.getPosition()[0]+(map->getWidth()/2), -orbCam1.getPosition()[1], -orbCam1.getPosition()[2]+(map->getLength()/2));
				glutPostRedisplay();
				break;
			case 'z': // FOVY zoom OUT
			  fovy += 0.9; 
			  glutPostRedisplay();
			break;
		   case 'Z':  // FOVY zoom IN
			  fovy -= 0.9; 
			  glutPostRedisplay();
			break;
		}
}
void keyboard (unsigned char key, int x, int y)
{  
	int count = 0;

	// camera controls in FREE CAMERA MODE
	if(currentCam == freeCam)
	{
		keyboardFreeCam(key);
	}
	// Orbiting Camera MODE
	else if (currentCam == orbitingCam)
	{
		keyboardOrbitingCam(key);
	}
	
	// Robot Camera mode
	if(ctrlUnit->onRobot() || currentCam == robotCam)
	{
		keyboardRobot(key);
		robotCam1 = (*robotIterator)->camera;
		glutPostRedisplay();
	}

    // default keys
	switch (key) {
		case 'r':   
			// Reset camera
			freecam.set(cugl::Point(map->getWidth()/2, (map->getLength() + 40) - map->getLength()/2, map->getLength() + 40), cugl::Point(map->getWidth()/2, 0, map->getLength()/2), cugl::Vector(0, 1, -1).unit());
			currentCam = freeCam;
			glutPostRedisplay();
			break;
		case '\\':
			if(!lightingEnabled)
				glEnable(GL_LIGHTING);
			else
				glDisable(GL_LIGHTING);
			lightingEnabled = !lightingEnabled;
			glutPostRedisplay();
			break;
		case 'l':
			ambientLightEnabled = !ambientLightEnabled;
			break;
		case ';':
			// NW post light
			lightPostLightsState[0] = !lightPostLightsState[0];
			glutPostRedisplay();
			break;
		case 39: // 34 is ascii for: "
			// NE post light
			lightPostLightsState[1] = !lightPostLightsState[1];
			glutPostRedisplay();
			break;
		case '.':
			// SW post light
			lightPostLightsState[2] = !lightPostLightsState[2];
			glutPostRedisplay();
			break;
		case '/':
			// SE post light
			lightPostLightsState[3] = !lightPostLightsState[3];
			glutPostRedisplay();
			break;
		case 'L':	// turn on/off all light posts
			if(allLightPostsSwitch)
			{
				for(int i=0; i<4; ++i)
				{
					lightPostLightsState[i] = 0;
				}
			}
			else
			{
				for(int i=0; i<4; ++i)
				{
					lightPostLightsState[i] = 1;
				}
			}
			allLightPostsSwitch = !allLightPostsSwitch;
			glutPostRedisplay();
			break;
		case 'i':   // control unit light
			cuLightOn = !cuLightOn;
			glutPostRedisplay();
			break;
		case 'k':   // Robot light ON
			robotLightOn = !robotLightOn;
			glutPostRedisplay();
			break;
		case 'm':
			// shading
			if(shadingMode==SMOOTH)
			{
				glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
				shadingMode = WIREFRAME;
				glutPostRedisplay();
			}
			else if(shadingMode==WIREFRAME)
			{
				glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
				glShadeModel (GL_FLAT);	
				shadingMode = FLAT;
				glutPostRedisplay();
			}
			else if(shadingMode==FLAT)
			{
				glShadeModel (GL_SMOOTH);	
				shadingMode = SMOOTH;
				glutPostRedisplay();
			}
			break;
		case 'c':
			// Circular Lighting
			orbitingCamLight = !orbitingCamLight;
			glutPostRedisplay();
			break;
		case 27:
			exit(0);
			break;
		case '1':
			// free cam
			currentCam = freeCam;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '2':
			// orbiting camera
			currentCam = orbitingCam;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '3':
			currentCam = lPostCamNW;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '4':
			currentCam = lPostCamNE;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '5':
			currentCam = lPostCamSW;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '6':
			currentCam = lPostCamSE;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case '7':	// CU camera
			currentCam = cuCam;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case 'h':
		case 'H':
			std::cout << "\n\n\n" << std::endl;
			helpPrompt = !helpPrompt;
			help();
			break;
			break;
		case 't':
			// texturing
			texturing = !texturing;
			glutPostRedisplay();
			break;
		case '<':
			if(!ctrlUnit->onRobot())
			{	
				ctrlUnit->moveUp();
			}
			else
			{
				ctrlUnit->detach();
			}
			glutPostRedisplay();
			break;
		case '>':
			if(!ctrlUnit->onRobot() && !ctrlUnit->onBuilding())
			{
				ctrlUnit->moveDown();
			}
			else
			{
			}
			glutPostRedisplay();
			break;
		case 'o':
			for(vector<Robot*>::iterator i = robots.begin(); i!=robots.end(); ++i)
				(*i)->switchTexture();
			break;
		default:
			break;
	}
}
void specialKey(int key, int x, int y) {
	switch(key) 
	{
		case GLUT_KEY_F1:
			if (robots.size() > 0){
				currentCam = robotCam;
				robotIterator = robots.begin();
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				rotationUp = 0.0;
				rotationLeft = 0.0;
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F2:
			if (robots.size() > 1)
			{
				currentCam = robotCam;
				robotIterator = robots.begin() + 1;
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				rotationUp = 0.0;
				rotationLeft = 0.0;
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F3:
			if (robots.size() > 2)
			{
				currentCam = robotCam;
				robotIterator = robots.begin() + 2;
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F4:
			if (robots.size() > 3){
				currentCam = robotCam;
				robotIterator = robots.begin() + 3;
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F5:
			if (robots.size() > 4)
			{
				currentCam = robotCam;
				robotIterator = robots.begin() + 4;
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F6:
			if (robots.size() > 5)
			{
				currentCam = robotCam;
				robotIterator = robots.begin() + 5;
				robotCam1 = (*robotIterator)->camera;
				fovy = 45; // reset fovy
				glutPostRedisplay();
			}
			break;
		case GLUT_KEY_F10:
			if(robotIterator == robots.begin())
				robotIterator = robots.end();
			--robotIterator;
			currentCam = robotCam;
			robotCam1 = (*robotIterator)->camera;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		case GLUT_KEY_F11:
			++robotIterator;
			if(robotIterator == robots.end())
				robotIterator = robots.begin();
			currentCam = robotCam;
			robotCam1 = (*robotIterator)->camera;
			fovy = 45; // reset fovy
			glutPostRedisplay();
			break;
		// For full screen. Toggles between the two.
		case GLUT_KEY_F7:
			if(!isFullScreen)
			{
				isFullScreen = 1;
				glutFullScreen();
			}
			else
			{
				isFullScreen = 0;
				glutReshapeWindow(1024, 768);
			}
			break;
		case GLUT_KEY_UP:
				ctrlUnit->moveBackward();
				glutPostRedisplay();
			break;
		case GLUT_KEY_DOWN:
				ctrlUnit->moveForward();
				glutPostRedisplay();
			break;
		case GLUT_KEY_RIGHT:
				ctrlUnit->moveLeft();
				glutPostRedisplay();
			break;
		case GLUT_KEY_LEFT:
				ctrlUnit->moveRight();
				glutPostRedisplay();
			break;
		default:
			break;
	}
}


//----------------------------------------------------------------------------
// write 2d text using GLUT
// The projection matrix must be set to orthogonal before call this function.
//
// source: http://www.songho.ca/opengl/gl_displaylist.html
//----------------------------------------------------------------------------
void drawString(const char *str, int x, int y, float color[4], void *font)
{
	// backup current model-view matrix
    glPushMatrix();                     // save current modelview matrix
    glLoadIdentity();                   // reset modelview matrix

    // set to 2D orthogonal projection
    glMatrixMode(GL_PROJECTION);        // switch to projection matrix
    glPushMatrix();                     // save current projection matrix
    glLoadIdentity();                   // reset projection matrix
    gluOrtho2D(0, 400, 0, 300);         // set to orthogonal projection

    glPushAttrib(GL_LIGHTING_BIT | GL_CURRENT_BIT); // lighting and color mask
    glDisable(GL_LIGHTING);     // need to disable lighting for proper text color

    glColor4fv(color);          // set text color
    glRasterPos2i(x, y);        // place text position

	int oldY = y;

    // loop all characters in the string
    while(*str)
    {

		if(*str =='\n')
		{
			y -= 6;
			glRasterPos2i(x, y);
		}
		else if(*str =='^')
		{
			x += 120;
			y = oldY;
			glRasterPos2i(x, y);
		}
		else
		{
			glutBitmapCharacter(font, *str);
		}
        ++str;
    }

    glEnable(GL_LIGHTING);
    glPopAttrib();

	// restore projection matrix
    glPopMatrix();                      // restore to previous projection matrix

    // restore modelview matrix
    glMatrixMode(GL_MODELVIEW);         // switch to modelview matrix
    glPopMatrix();                      // restore to previous modelview matrix
}

//----------------------------------------------------------------------------
// display frame rates
//
// source: http://www.songho.ca/opengl/gl_displaylist.html
//----------------------------------------------------------------------------
void showFPS()
{
    static Timer timer;
    static int count = 0;
    static stringstream ss;
    double elapsedTime;

    float color[4] = {1, 1, 0, 1};

    // update fps every second
    elapsedTime = timer.getElapsedTime();
    if(elapsedTime < 1.0)
    {
        ++count;
    }
    else
    {
        ss.str("");
        ss << std::fixed << std::setprecision(1);
        ss << (count / elapsedTime) << " FPS" << ends; // update fps string
        ss << std::resetiosflags(std::ios_base::fixed | std::ios_base::floatfield);
        count = 0;                      // reset counter
        timer.start();                  // restart timer
    }
    drawString(ss.str().c_str(), 315, 286, color, font);


}

//---------------------------------------------------------
// Initializer 
//---------------------------------------------------------
void init()
{
	//Load identity modelview
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//load message
	std::cout << "\n\nHold your pants, the game is loading...";

	//shadign states
	glShadeModel (GL_SMOOTH);
	glClearColor (0.0, 0.0, 0.0, 0.0);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	//Depth Sates
	glClearDepth(1.0f);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_CULL_FACE);

	//Noramlize normals
	glEnable(GL_NORMALIZE);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glCullFace(GL_BACK);
	glEnable(GL_LIGHTING);



	// intitialize objects
	if(!map)
		map = new Map(currentLvl);

	// set up the cameras based on the map
	freecam.setSkyBoxSize(map->getSkyBoxSize());
	freecam.set(cugl::Point(map->getWidth()/2, (map->getLength() + 40) - map->getLength()/2, map->getLength() + 40), cugl::Point(map->getWidth()/2, 0, map->getLength()/2), cugl::Vector(0, 1, -1).unit());
	orbCam1.set(cugl::Point(map->getWidth()/2, (map->getLength() + 40) - map->getLength()/2, map->getLength() + 40), cugl::Point(map->getWidth()/2, 0, map->getLength()/2));
	
	// lights
	configureLights();

	// CU
	ctrlUnit = new ControlUnit(map);
	//ctrlUnit->setBoundaries(map->getWidth()-1,map->getLength()-1);
	ctrlUnit->setLocation(8, 0, map->getLength()-7);

	ctrlUnit->setMapRobots(&mapRobots);
	//ctrlUnit->setID(3);

	buildRobot();
	

	std::cout << " Done!\n\n" << std::endl;

	glutWarpPointer(windowWidth / 2, windowHeight / 2);

}



//---------------------------------------------------------
// IDLE 
//---------------------------------------------------------
void idle()
{
	for(vector<Robot*>::iterator i = robots.begin(); i!=robots.end(); ++i)
		(*i)->animate();
	ctrlUnit->animate();
	glutPostRedisplay();
}



//---------------------------------------------------------
// BUILD ROBOT
//---------------------------------------------------------
void buildRobot()
{
	// Good Robot
	Robot *robot = new Robot(map);
	robot->setID(1);
	robot->addTracks();
	robot->addCannon(1);
	//robot->addEBrain(2);
	robot->addMissile(2);
	robot->rotate(180);
	robot->finished();
	//robot->setLocation(map->getWidth() - 10, 0, 3);
	robot->setLocation(10, 0, map->getLength() - 3);
	
	// Bad Robot
	Robot *robot2 = new Robot(map);
	robot2->random();
	robot2->setID(2);
	robot2->addBipod();
	robot2->addCannon(1);
	robot2->addPhaser(3);
	robot2->addMissile(2);
	robot2->addNuclear(4);
	//robot2->rotate(180);
	robot2->finished();
	//robot2->setLocation(10, 0, map->getLength() - 3);
	robot2->setLocation(map->getWidth() - 10, 0, 3);

	/*
	Robot *robot3 = new Robot(map);
	robot3->random();
	robot3->setID(3);
	robot3->addAntiGrav();
	robot3->addPhaser(1);
	robot3->addNuclear(2);
	robot3->rotate(180);
	robot3->finished();
	robot3->setLocation(12, 0, map->getLength() - 3);
	*/
	robots.push_back(robot);
	robots.push_back(robot2);
//	robots.push_back(robot3);
	for(std::vector<Robot*>::iterator i = robots.begin(); i<robots.end(); ++i)
	{
		mapRobots[(*i)->getID()] = (*i);
	}
	robot->setMapRobots(&mapRobots);
	robot2->setMapRobots(&mapRobots);
//	robot3->setMapRobots(&mapRobots);

	currentIDRobot = 1;
	robotIterator = robots.begin();
}


void resetTheGame()
{
	for(vector<Robot*>::iterator i = robots.begin(); i!=robots.end();)
	{
		delete *i;
		i = robots.erase(i);
	}
	mapRobots.clear();
	delete ctrlUnit;
	gameOver = 0;
	ambientLightEnabled = 1;
	for(int i=0; i<4; ++i)
	{
		lightPostLightsState[i] = 1;
	}
	cuLightOn = 1;
	orbitingCamLight = 1;
}

void reloadTheGame(std::string lvl)
{
	resetTheGame();
	if(lvl != currentLvl)
	{
		delete map;
		map = 0;
		currentLvl = lvl;
	}
	init();
}

void menu(int i)
{
	switch(i)
	{
	case 0:
		resetTheGame();
		init();
		glutPostRedisplay();
		break;
	case 1:
		reloadTheGame("level1.txt");
		std::cout<<"level 1 loaded"<<std::endl;
		glutPostRedisplay();
		break;
	case 2:
		reloadTheGame("level2.txt");
		std::cout<<"level 2 loaded"<<std::endl;
		glutPostRedisplay();
		break;
	case 32:
		exit(0);
		break;
	}
}


//---------------------------------------------------------
// MAIN
//---------------------------------------------------------

int main(int argc, char** argv)
{
	currentLvl = "level1.txt";
	// print key options
	help();
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);
	glutInitWindowSize (windowWidth, windowHeight); 
	glutInitWindowPosition (400, 0);
	mainWindow = glutCreateWindow ("Open Robot Battle Near Earth - Team 7");
	glutDisplayFunc(display);
	//glutDestroyWindow();
	init();
	
	int submenu1;
	// submenu
	submenu1 = glutCreateMenu(menu);
	glutAddMenuEntry("level 1", 1);
	glutAddMenuEntry("level 2", 2);

	// menu
	glutCreateMenu(menu);
	glutAddMenuEntry("Restart", 0);
	glutAddSubMenu("Choose level", submenu1);
	glutAddMenuEntry("Exit", 32);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
	
	glutDisplayFunc(display);
	glutIdleFunc(idle);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutSpecialFunc(specialKey);
	glutMainLoop();
	glutMainLoop();

   return 0;
}